package com.xyy.saas.payment.util.retry;

import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.common.exception.RetryException;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.trace.AlarmService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ThreadLocalRandom;

/**
 * @author wangzeqi
 */
@Slf4j
public class ExponentialBackoffRetryWithJitter {

    /**
     * 初始重试间隔 单位毫秒
     */
    private final long initialInterval;

    /**
     * 重试间隔 指数增长因子
     */
    private final double multiplier;

    /**
     * 最大重试间隔 单位毫秒
     */
    private final long maxInterval;

    /**
     * 最大重试次数
     */
    private final int maxRetries;

    private final AlarmService alarmService;

    // 本地重试 最大抖动时间不宜过大
    private final static int maxJitter = 10 * 1000;

    public ExponentialBackoffRetryWithJitter(long initialInterval, double multiplier, long maxInterval, int maxRetries, AlarmService alarmService) {
        this.initialInterval = initialInterval;
        this.multiplier = multiplier;
        this.maxInterval = maxInterval;
        this.maxRetries = maxRetries;
        this.alarmService = alarmService;
    }

    @SneakyThrows
    public void executeWithRetry(Runnable task, RetryTradeType type, Boolean maxRetriesAlarm) {
        int attempt = 0;
        long currentInterval = initialInterval;

        while (attempt <= maxRetries) {
            try {
                task.run(); // 执行任务
                return;     // 如果任务成功，则退出
            } catch (Exception e) {
                if (!(e instanceof RetryException)) {
                    log.error("executeWithRetry error. {}", e.getMessage(), e);
                    throw e;
                }
                attempt++;
                if (attempt > maxRetries) { // 超出最大重试次数
                    String errMsg = e.getMessage();
                    log.error("Max retries executeWithRetry error. {}", errMsg, e);
                    if (Boolean.TRUE.equals(maxRetriesAlarm)) {
                        alarmService.asyncAlarm(type.getDesc() + "失败", "executeWithRetry", "重试次数：" + maxRetries, errMsg);
                    }
                    throw new PaymentException(ResultCodeEnum.EXECUTE_WITH_RETRY_ERROR, errMsg);
                }

                // 添加随机抖动
                long jitter = ThreadLocalRandom.current().nextLong(currentInterval / 2, currentInterval);
                // log.error("Retry attempt {} failed. Retrying in {} ms (with jitter)...", attempt, jitter);

                // 等待当前间隔时间 + 抖动
                try {
                    Thread.sleep(Math.min(jitter, maxJitter));
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Retry interrupted", ex);
                }

                // 增加间隔时间，应用指数增长
                currentInterval = Math.min((long) (currentInterval * multiplier), maxInterval);
            }
        }
    }


    public static void main(String[] args) {
        RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> {
            System.out.println("Attempting task...");
            if (Math.random() > 0.3) { // 模拟 70% 的失败率
                throw new RetryException("Task failed!");
            }
            System.out.println("Task succeeded!");
        }, RetryTradeType.PINGAN_CREDIT_REFUND, false);
        System.out.println("Finished task!");
    }

}
